﻿//-------------------------------------
// WBFSSync - WBFSSync.exe
//
// Copyright 2009 Caian (ÔmΣga Frøst) <frost.omega@hotmail.com> :
//
// WBFSSync is Licensed under the terms of the Microsoft Reciprocal License (Ms-RL)
//
// PathBox.cs:
//
// Controle que imita a caixa de caminho do Explorer
//
//-------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace WBFSSync.AeroControls
{
    struct PATHSEGMENT
    {
        public String name;
        public Int32 width;

        public PATHSEGMENT(string n)
        {
            name = n;
            width = TextRenderer.MeasureText(name, Skin.CaptionFont).Width + 16;
        }

        public override string ToString()
        {
            return name + " - " + width;
        }
    }

    public delegate void PathEvent(object sender, string path);

    public partial class PathBox : UserControl
    {
        //---------------------- Constantes
        
        //---------------------- Variáveis

        Boolean mouseLeftClick = false;
        Boolean mouseOverControl = false;
        Boolean mouseOverRefresh = false;
        Int32 pathSegmentUnderMouse = -1; //Nenhum segmento selecionado
        Point mousePosition = new Point(-1, -1);

        string path = "";
        public String Path { get { return path; } set { path = value; OnPathChanged(); } }

        PATHSEGMENT[] segments;
        int startSegment = 0;

        Image folderIcon = null;

        StringFormat textFormat = new StringFormat();

        public event PathEvent RefreshClick;
        public event PathEvent PathClick;

        //---------------------- Rotinas
        public PathBox()
        {
            InitializeComponent();
            DoubleBuffered = true;

            textFormat.Alignment = StringAlignment.Near;
            textFormat.LineAlignment = StringAlignment.Center;
            textFormat.Trimming = StringTrimming.EllipsisCharacter;
        }

        public bool CheckMouseIntersection(int x, int y, int w, int h)
        {
            return ((mousePosition.X >= x) && (mousePosition.X < x + w) &&
                    (mousePosition.Y >= y) && (mousePosition.Y < y + h));
        }

        public bool CheckMouseIntersection1D(int x, int w)
        {
            return ((mousePosition.X >= x) && (mousePosition.X < x + w));
        }

        //---------------------

        protected override void OnMouseEnter(EventArgs e)
        {
            mouseOverControl = true;
            base.OnMouseEnter(e);

            Invalidate();
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            mousePosition = new Point(-1, -1);
            pathSegmentUnderMouse = -1;
            mouseOverControl = false;
            mouseLeftClick = false;
            base.OnMouseLeave(e);

            Invalidate();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            mousePosition = e.Location;
            base.OnMouseMove(e);

            bool newMouseOverRefresh = (mousePosition.X >= Width - 26);
            if (mouseOverRefresh != newMouseOverRefresh)
            {
                mouseOverRefresh = newMouseOverRefresh;
                Invalidate();
            }

            int x = 37;
            int lastPathSegmentUnderMouse = pathSegmentUnderMouse;
            pathSegmentUnderMouse = -1;

            for (int i = startSegment; i < segments.Length; i++)
            {
                if (CheckMouseIntersection1D(x, segments[i].width))
                {
                    pathSegmentUnderMouse = i;
                    break;
                }
                x += segments[i].width;
            }

            if (pathSegmentUnderMouse != lastPathSegmentUnderMouse) Invalidate();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left) mouseLeftClick = true;
            base.OnMouseDown(e);

            Invalidate();
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                mouseLeftClick = false;

                if (pathSegmentUnderMouse >= 0)
                {
                    OnPathClick();
                }
                else if(mouseOverRefresh)
                {
                    OnRefreshClick();
                }
            }

            base.OnMouseUp(e);

            Invalidate();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            Invalidate();
        }

        //---------------------

        protected override void OnPaint(PaintEventArgs e)
        {
            if (!Skin.SkinReady)
            {
                e.Graphics.DrawLine(Pens.Blue, 0, 0, Width, Height);
                e.Graphics.DrawLine(Pens.Blue, Width, 0, 0, Height);

                e.Graphics.DrawLine(Pens.Blue, 0, 0, Width, 0);
                e.Graphics.DrawLine(Pens.Blue, 0, Height - 1, Width, Height - 1);
                e.Graphics.DrawLine(Pens.Blue, 0, 0, 0, Height);
                e.Graphics.DrawLine(Pens.Blue, Width - 1, 0, Width - 1, Height);

                return;
            }

            e.Graphics.SmoothingMode = SmoothingMode.HighSpeed;
            e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
            e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

            DrawBackground(e);
            DrawRefresh(e);
            DrawFolderIcon(e);
            DrawPathSegments(e);

            base.OnPaint(e);
        }

        private void DrawRefresh(PaintEventArgs e)
        {
            if (!mouseOverControl)
            {
                e.Graphics.DrawImage(Skin.pathbox_refresh, Width - 26, 0, 26, 22);
            }
            else
            {
                if (mouseOverRefresh)
                {
                    if (mouseLeftClick)
                        e.Graphics.DrawImage(Skin.pathbox_refresh_c, Width - 26, 0, 26, 22);
                    else
                        e.Graphics.DrawImage(Skin.pathbox_refresh_o, Width - 26, 0, 26, 22);
                }
                else
                {
                    e.Graphics.DrawImage(Skin.pathbox_refresh_s, Width - 26, 0, 26, 22);
                }
            }
        }

        private void DrawBackground(PaintEventArgs e)
        {
            e.Graphics.DrawImage(Skin.pathbox_l, 0, 0, 2, 22);

            if (!mouseOverControl) 
                e.Graphics.DrawImage(Skin.pathbox_m, 2, 0, Width - 28, 22);
            else 
                e.Graphics.DrawImage(Skin.pathbox_m_s, 2, 0, Width - 28, 22);
        }

        private void DrawFolderIcon(PaintEventArgs e)
        {
            if (folderIcon != null) e.Graphics.DrawImage(folderIcon, 3, 3, 16, 16);
            e.Graphics.DrawImage(Skin.pathbox_separator_arrow, 26, 8, 5, 7);
        }

        private void DrawPathSegments(PaintEventArgs e)
        {
            if (segments != null)
            {
                int x = 37;
                for (int i = startSegment; i < segments.Length; i++)
                {
                    DrawPathSegment(e, i, x);
                    x += segments[i].width;
                }
            }
        }

        private void DrawPathSegment(PaintEventArgs e, int i, int x)
        {
            RectangleF rect = new RectangleF(x + 4, 2, Math.Min(segments[i].width - 4, Width - x), Height - 2);

            if (pathSegmentUnderMouse == i)
            {
                if (mouseLeftClick)
                {
                    rect.X += 1;
                    rect.Y += 1;
                    rect.Width -= 1;

                    e.Graphics.DrawImage(Skin.pathbox_selection_l_c, x, 1, 2, 20);
                    e.Graphics.DrawImage(Skin.pathbox_selection_m_c, x + 2, 1, segments[i].width - 18, 20);
                    e.Graphics.DrawImage(Skin.pathbox_selection_r_c, x + segments[i].width - 16, 1, 16, 20);
                    e.Graphics.DrawImage(Skin.pathbox_separator_arrow, x + segments[i].width - 9, 9, 5, 7);

                    e.Graphics.DrawString(segments[i].name, Skin.CaptionFont, Brushes.Black, rect, textFormat);
                }
                else
                {
                    e.Graphics.DrawImage(Skin.pathbox_selection_l_o, x, 1, 2, 20);
                    e.Graphics.DrawImage(Skin.pathbox_selection_m_o, x + 2, 1, segments[i].width - 18, 20);
                    e.Graphics.DrawImage(Skin.pathbox_selection_r_o, x + segments[i].width - 16, 1, 16, 20);
                    e.Graphics.DrawImage(Skin.pathbox_separator_arrow, x + segments[i].width - 10, 8, 5, 7);
                    
                    e.Graphics.DrawString(segments[i].name, Skin.CaptionFont, Brushes.Black, rect, textFormat);
                }
            }
            else
            {
                e.Graphics.DrawString(segments[i].name, Skin.CaptionFont, Brushes.Black, rect, textFormat);
                e.Graphics.DrawImage(Skin.pathbox_separator_arrow, x + segments[i].width - 10, 8, 5, 7);
            }
        }

        private void UpdateFolderIcon()
        {
            if (path.Length == 3) //Drive
            {
                IDrive d = DeviceManager.GetDrive(path);
                if (d == null)
                {
                    folderIcon = Skin.icon_16x16_folder;
                }
                else
                {
                    folderIcon = ExplorerListView.GetSmallDriveIcon(d);
                }
            }
            else
            {
                switch (path)
                {
                    case FormWBFSSync.path_computer:
                        folderIcon = Skin.icon_16x16_computer;
                        break;

                    case FormWBFSSync.path_library:
                        folderIcon = Skin.icon_16x16_library;
                        break;

                    default:
                        folderIcon = Skin.icon_16x16_folder;
                        break;
                }
            }
        }

        private void UpdatePathSegments()
        {
            segments = null;
            if (path == String.Empty) return;

            switch (path)
            {
                case FormWBFSSync.path_computer:
                    segments = new PATHSEGMENT[1];
                    segments[0] = new PATHSEGMENT(Language.foldername_computer);
                    goto CALCULATESTARTSEG;
                
                case FormWBFSSync.path_covers:
                    segments = new PATHSEGMENT[1];
                    segments[0] = new PATHSEGMENT(Language.foldername_covers);
                    goto CALCULATESTARTSEG;
                
                case FormWBFSSync.path_library:
                    segments = new PATHSEGMENT[1];
                    segments[0] = new PATHSEGMENT(Language.foldername_library);
                    goto CALCULATESTARTSEG;
            }

            String[] sSegments = path.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
            segments = new PATHSEGMENT[sSegments.Length + 1];
            segments[0] = new PATHSEGMENT(Language.foldername_computer);
            segments[1] = new PATHSEGMENT(FormWBFSSync.FormatDriveName(DeviceManager.GetDrive(sSegments[0] + '\\')));

            for (int i = 1; i < sSegments.Length; i++)
            {
                segments[i + 1] = new PATHSEGMENT(sSegments[i]);
            }

            CALCULATESTARTSEG:

            startSegment = 0;
            int j = 0;
            int w = Width - 63;
            for (int i = segments.Length - 1; i >= 0; i--)
            {
                w -= segments[i].width;
                if (w < 0)
                    break;

                j++;
            }

            j = Math.Max(j, 1);

            startSegment = segments.Length - j;
        }

        //---------------------

        private void OnPathChanged()
        {
            UpdateFolderIcon();
            UpdatePathSegments();
            Invalidate();
        }

        private void OnRefreshClick()
        {
            if (RefreshClick != null) RefreshClick(this, path);
        }

        private void OnPathClick()
        {
            //Verifica se o segmento não é uma pasta especial
            if (pathSegmentUnderMouse == 0)
            {
                if (segments.Length == 1) //O primeiro segmento é a própria variável 'path'
                {
                    if (PathClick != null) PathClick(this, path);
                    return;
                }
                else //O primeiro segmento é o atalho para 'Meu Computador'
                {
                    if (PathClick != null) PathClick(this, FormWBFSSync.path_computer);
                    return;
                }
            }

            //Reconstroi o path, começa com o drive, que não pode ser pego na matriz segment[] pois nela o nome
            //do drive está formatado
            StringBuilder newPath = new StringBuilder(path.Substring(0, 2), path.Length); //Talvez não use todo o tamanho

            if (pathSegmentUnderMouse == 1) //Só o drive
            {
                newPath.Append('\\');
            }
            else
            {

                //Primeiro segmento é 'Meu Computador' e o segundo é o nome do drive, então começa pelo terceiro
                for (int i = 2; i <= pathSegmentUnderMouse; i++)
                {
                    newPath.Append('\\');
                    newPath.Append(segments[i].name);
                }
            }

            if (PathClick != null) PathClick(this, newPath.ToString());
        }
    }
}
